package com.yehun.config;


import com.alibaba.fastjson.serializer.SerializerFeature;
import com.yehun.utils.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.TimeoutUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.ShardedJedisPool;

import javax.annotation.PostConstruct;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
@Repository("redisDataSource")
public class RedisDataSource {

    private static final Logger logger = LoggerFactory.getLogger(RedisDataSource.class);

    /**
     * 模糊匹配字符
     */
    private static final String VAGUE_MATCH_PATTERN = "*";

    private static final SerializerFeature[] SERIALIZER_FEATURES = {
        SerializerFeature.WriteClassName,
        SerializerFeature.WriteDateUseDateFormat, //格式化日期
        SerializerFeature.WriteMapNullValue, //空值字段输出
        SerializerFeature.WriteNullStringAsEmpty, //字符串为null时输出""
        SerializerFeature.WriteNullListAsEmpty, //列表为null时输出[]
        SerializerFeature.WriteNullBooleanAsFalse, //boolean类型为null时输出false
        SerializerFeature.WriteNullNumberAsZero //整型为null时输出0
    };

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private ShardedJedisPool shardedJedisPool;
    public static ShardedJedisPool getShardedJedisPool() {
        return redisDataSource.shardedJedisPool;
    }

    private static RedisDataSource redisDataSource;
    @PostConstruct
    public void init() {
        redisDataSource = this;
        redisDataSource.redisTemplate = this.redisTemplate;
        redisDataSource.shardedJedisPool = this.shardedJedisPool;
    }

    /**
     * 切换redis库
     */
    public static RedisDataSource select(int dbIndex) {
        redisDataSource.redisTemplate.execute((RedisCallback<Void>) redisConnection -> {
            redisConnection.select(dbIndex);
            return null;
        });
        return redisDataSource;
    }

    public static Long getDb() {
        return redisDataSource.redisTemplate.execute((RedisCallback<Long>) redisConnection -> ((Jedis)redisConnection.getNativeConnection()).getDB());
    }

    public static void selectDefaultDb() {
        redisDataSource.redisTemplate.execute((RedisCallback<Void>) redisConnection -> {
            int defualtDb = Integer.parseInt(((Jedis)redisConnection.getNativeConnection()).getDB().toString());
            redisConnection.select(defualtDb);
            return null;
        });
    }

    public static Object get(String key) {
        final byte[] newKey = key.getBytes();
        byte[] bytes = redisDataSource.redisTemplate.execute((RedisCallback<byte[]>) redisConnection -> redisConnection.get(newKey));
        if(bytes == null) {
            return null;
        }
        logger.info("redis命中 => " + key);
        return JsonUtils.toObject(bytes);
    }

    @SuppressWarnings("unchecked")
    public static <T> T get(String key, Class<T> type) {
        final byte[] newKey = key.getBytes();
        byte[] bytes = redisDataSource.redisTemplate.execute((RedisCallback<byte[]>) redisConnection -> redisConnection.get(newKey));
        if(bytes == null) {
            return null;
        }
        //logger.info("redis命中 => " + key);
        //logger.info("redis命中Type => " + type.getName());
        return (T) JsonUtils.toObject(bytes);
    }

    public static void set(String key, Object object) {
        if(object == null) {
            return;
        }
        //logger.info("redis添加 => " + key);
        final byte[] newKey = key.getBytes();
        final byte[] newValue = JsonUtils.toBytes(object, SERIALIZER_FEATURES);
        redisDataSource.redisTemplate.execute((RedisCallback<Boolean>) redisConnection -> redisConnection.setNX(newKey, newValue));
    }

    public static void set(String key, Object object, long seconds) {
        if(object == null) {
            return;
        }
        logger.info("redis添加 => " + key);
        final byte[] newKey = key.getBytes();
        final byte[] newValue = JsonUtils.toBytes(object, SERIALIZER_FEATURES);
        redisDataSource.redisTemplate.execute((RedisCallback<Boolean>) redisConnection -> {
            expire(redisConnection, newKey, seconds, TimeUnit.SECONDS);
            return redisConnection.setNX(newKey, newValue);
        });
    }

    public static void set(String key, Object object, long time, TimeUnit unit) {
        if(object == null) {
            return;
        }
        logger.info("redis添加 => " + key);
        final byte[] newKey = key.getBytes();
        final byte[] newValue = JsonUtils.toBytes(object, SERIALIZER_FEATURES);
        redisDataSource.redisTemplate.execute((RedisCallback<Boolean>) redisConnection -> {
            expire(redisConnection, newKey, time, unit);
            return redisConnection.setNX(newKey, newValue);
        });
    }

    /**
     * 设置过期时间
     */
    private static void expire(RedisConnection redisConnection, byte[] key, long time, TimeUnit unit) {
        try {
            redisConnection.pExpire(key, TimeoutUtils.toMillis(time, unit));
        } catch (Exception e) {
            redisConnection.expire(key, TimeoutUtils.toMillis(time, unit));
        }
    }

    /**
     * key是否存在
     */
    public static boolean exists(String key) {
        return redisDataSource.redisTemplate.execute((RedisCallback<Boolean>) redisConnection -> {
            return redisConnection.exists(key.getBytes());
        });
    }

    /**
     * 删除
     */
    public static void remove(String key) {
        redisDataSource.redisTemplate.delete(key);
    }

    /**
     * 模糊删除
     */
    public static void removePattern(String key) {
        redisDataSource.redisTemplate.delete(redisDataSource.redisTemplate.keys(key + VAGUE_MATCH_PATTERN));
    }

    /**
     * 批量删除
     */
    public static void removeBatch(String... keys) {
        for (String key : keys) {
            redisDataSource.redisTemplate.delete(key);
        }
    }

    /**
     * 批量模糊删除
     */
    public static void removeBatchPattern(String... keys) {
        for (String key : keys) {
            redisDataSource.redisTemplate.delete(redisDataSource.redisTemplate.keys(key + VAGUE_MATCH_PATTERN));
        }
    }

    /**
     * 获取所有的key
     */
    public static Set<String> getKeys() {
        return redisDataSource.redisTemplate.keys(VAGUE_MATCH_PATTERN);
    }

    /**
     * 获取匹配的key
     */
    public static Set<String> getKeys(String key) {
        return redisDataSource.redisTemplate.keys(key + VAGUE_MATCH_PATTERN);
    }

    /**
     * 获取key的过期时间
     */
    public static long getKeyExpired(String key) {
        return redisDataSource.redisTemplate.getExpire(key);
    }

}